package cn.good.yan;

import cn.good.yan.entity.COrder;
import cn.good.yan.mapper.COrderMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.math.BigDecimal;
import java.util.List;

/**
 * 水平分片 - 水平分表 的测试
 * 引用：application4.properties 配置文件
 * 准备：server-order1 数据源中有2个表（t_order0 和 t_order1）和 server-order2数据源中有2个表（t_order0 和 t_order1）
 *
 * @author shengren.yan
 * @create 2024/12/8
 */
@SpringBootTest
public class SplitTest4 {

    @Autowired
    private COrderMapper cOrderMapper;

    /**
     * 水平分表 - 写入数据的测试 - 分表插入数据
     * 有2个数据库 （order1库 order2库）分别有2个表（t_order0 和 t_order1）表
     * 写操作，分别插入对应的数据库的sql
     * 会打印2条SQL 语句：Logic SQL（逻辑SQL） - Actual SQL（具体操作的库）
     */
    @Test
    public void testInsert() {

        // 1.先分库
        // 分片规则：表中`user_id`为偶数时，数据插入`server-order1数据源`，`user_id`为奇数时，数据插入`server-order2数据源`
        // 2.再分表
        // 分片规则：表中`order_no`是字符串，进行哈希取模，哈希值为偶数时，插入到t_order0中，奇数插入到t_order1表中

        // 1 因user_id是1，奇数，先插入到 server-order2库中
        // 2 因order_no是取模，奇数，先插入到 t_order1表中
        // 逻辑SQL（Logic SQL） ：插入 t_c_order表
        // 实际SQL（Actual SQ） ：插入 server-order2 数据源 t_order1、t_order2 表
        for (long i = 1; i < 5; i++) {
            COrder order = new COrder();
            order.setOrderNo("yanyan" + i);
            order.setUserId(1L);
            order.setAmount(new BigDecimal(i));
            cOrderMapper.insert(order);
        }


        // 1 因user_id是2，偶数，先插入到 server-order1库中
        // 2 因order_no是取模，奇数，先插入到 t_order1表中
        // 逻辑SQL（Logic SQL） ：插入 t_c_order表
        // 实际SQL（Actual SQ） ：插入 server-order1 数据源 t_order1、t_order2 表
        for (long i = 5; i < 9; i++) {
            COrder order = new COrder();
            order.setOrderNo("yanyan" + i);
            order.setUserId(2L);
            order.setAmount(new BigDecimal(i));
            cOrderMapper.insert(order);
        }

        // 下面的testHash，分别插入到，哈希取模后的，对应的库中
        // i 对应的 Amount，方便看下面的插入对应表的数据
    }


    /**
     * 分别查看他们的hash值，取模后的是：
     * --- server-order2库中
     * 1  -1    --- t_order1 表中
     * 2   0    --- t_order0
     * 3  -1    --- t_order1 表中
     * 4   0    --- t_order0
     * --------------
     * server-order1库中
     * 5  -1   --- t_order1 表中
     * 6   0   --- t_order0
     * 7  -1   --- t_order1 表中
     * 8   0   --- t_order0
     */
    @Test
    public void testHash() {
        for (long i = 1; i < 5; i++) {
            String str = "yanyan" + i;
            System.out.println(str.hashCode() % 2);
        }
        System.out.println("--------------");
        for (long i = 5; i < 9; i++) {
            String str = "yanyan" + i;
            System.out.println(str.hashCode() % 2);
        }
    }


    /**
     * 水平分片：查询所有记录
     * 查询了两个数据源，每个数据源中使用 UNION ALL连接两个表
     */
    @Test
    public void testAll() {
        // 逻辑SQL（Logic SQL） ：查询 - SELECT  id,order_no,user_id,amount  FROM t_c_order
        // 实际SQL（Actual SQ） ：查询 -
        //                      先查：server-order1 库中的
        //                          SELECT  id,order_no,user_id,amount  FROM t_order0
        //                          UNION ALL SELECT  id,order_no,user_id,amount  FROM t_order1
        //                      再查：server-order2 库中的
        //                          SELECT  id,order_no,user_id,amount  FROM t_order0
        //                          UNION ALL SELECT  id,order_no,user_id,amount  FROM t_order1
        // 最后，把数据拼装在一起。UNION ALL的方式。
        List<COrder> orders = cOrderMapper.selectList(null);
        orders.forEach(System.out::println);
    }


    /**
     * 水平分片：（带条件查询） 根据 user_id 查询记录
     * 查询了一个数据源，每个数据源中使用UNION ALL连接两个表
     */
    @Test
    public void testShardingSelectByUserId() {
        // 因为分片规则有：user_id，会直接查询 server-order2 库中（t_order0 和 t_order1 表）
        // 逻辑SQL（Logic SQL） ：查询 - SELECT  id,order_no,user_id,amount  FROM t_c_order WHERE (user_id = ?)
        // 实际SQL（Actual SQ） ：查询 - 会根据分片规则，查询对应的库
        //                      SELECT  id,order_no,user_id,amount  FROM t_order0 WHERE (user_id = ?)
        //                      UNION ALL
        //                      SELECT  id,order_no,user_id,amount  FROM t_order1 WHERE (user_id = ?)
        QueryWrapper<COrder> orderQueryWrapper = new QueryWrapper<>();
        orderQueryWrapper.eq("user_id", 1L);
        List<COrder> orders = cOrderMapper.selectList(orderQueryWrapper);
        orders.forEach(System.out::println);
    }


    /**
     * 测试：分布式主键
     * 需要在实体，开启  @TableId(type = IdType.AUTO)，去掉   @TableId(type = IdType.ASSIGN_ID)
     * 配置文件中，需要配置对应的算法。 SNOWFLAKE 算法算法 、UUID 内置的uuid
     * application4.properties 配置文件中配置。
     */
    @Test
    public void testSnowflake() {
        // 再执行新增创建数据，查看表中的 主键id，会有新的值。（生成分布式id）
        testInsert();
    }

}
